/*
 * Copyright (C) 2010 ST-Ericsson AS
 * Author: Erwan Bracq / erwan.bracq@stericsson.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 */

#include <mid_statemachine.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <private/android_filesystem_config.h>

#include <sys/wait.h>
#include <dlfcn.h>
#include <time.h>

#include <atchannel.h>

#include <mid.h>
#include <mid_caif.h>
#include <mid_error.h>
#include <mid_flash.h>
#include <mid_gpio.h>
#include <mid_ipc.h>
#include <mid_log.h>
#include <mid_mfa.h>
#include <mid_power.h>
#include <crash_dump.h>

#include "mid_dev.h"
#include "mid_sys.h"

#define COUNTOF(A) (sizeof(A)/sizeof((A)[0]))

typedef enum MID_Event_Oper_e {
	MID_CLEAR_EVENT,
	MID_KEEP_EVENT,
} MID_Event_Oper_t;

typedef MID_Event_Oper_t (*Transition_T)(struct mid * mid_info_p);

typedef struct {
	MID_Event_t  event;
	Transition_T func;
} Event_Func_t;

typedef struct {
	Event_Func_t *event_func_info;
	int          timeout_value;
} State_Entry_t;

static mid_fw_upgr_error_code_t mid_fw_upgr_errors[] = {
	{"SYSTEM_ERROR",       MID_FW_UPGR_RSLT_SYSTEM_ERROR},
	{"INVALID_REQ_PHASE",  MID_FW_UPGR_RSLT_INVALID_REQ_PHASE},
	{"REQ_IN_PROGRESS",    MID_FW_UPGR_RSLT_REQ_IN_PROGRESS},
	{"UNSUPP_MODEM",       MID_FW_UPGR_RSLT_UNSUPP_MODEM},
	{"MODEM_SHUTDOWN",     MID_FW_UPGR_RSLT_MODEM_SHUTDOWN},
	{"AT_TIMEOUT",         MID_FW_UPGR_RSLT_AT_TIMEOUT},
	{"REQ_REJECTED",       MID_FW_UPGR_RSLT_REJECTED},
	{"AT_CHN_ERROR",       MID_FW_UPGR_RSLT_AT_CHN_ERROR},
	{"AT_CMD_ERROR",       MID_FW_UPGR_RSLT_AT_CMD_ERROR},
	{"INVALID_MODEM_STATE",MID_FW_UPGR_RSLT_INVALID_MODEM_STATE},
	{"FILESYS_ERROR",      MID_FW_UPGR_RSLT_FS_ERROR},
	{"IMG_SIZE_ERROR",     MID_FW_UPGR_RSLT_IMG_SIZE_ERROR},
	{"LOW_BATTERY",        MID_FW_UPGR_RSLT_LOW_BATT},
	{"NO_FW_IMG",          MID_FW_UPGR_RSLT_NO_FW_IMG},
};

static const char * const MID_EventNames[] =
{
	"MID_IPC_RBT_EVT",
	"MID_GPIO_RST2_EVT",
	"MID_GPIO_PRST_EVT",
	"MID_MODEM_HALT_EVT",
	"MID_MODEM_START_EVT",
	"MID_SIG_KILL_EVT",
	"MID_LONG_OPS_EVT",
	"MID_CFRDY_EVT",
	"MID_HIGH_TEMPERATURE_EVT",
	"MID_LOW_BATTERY_EVT",
	"MID_AT_CFUN0_EVT",
	"MID_AT_CFUN100_EVT",
	"MID_IPC_FW_UPGR_IMG_XFER_EVT",
	"MID_IPC_FW_UPGR_FLASH_EVT",
	"MID_IPC_FW_UPGR_STATUS_EVT",
	"MID_FW_XFER_OPS_EVT",
	"MID_FW_XFER_PROGRESS_EVT",
	"MID_TIMEOUT_EVT",
	"MID_FLASH_START_EVT",
	"MID_FLASH_END_EVT",
	"MID_LAST_EVT",
};

static const char * const MID_StateNames[] =
{
	"STATE_OFF",
	"STATE_BOOT",
	"STATE_MODINF_UP",
	"STATE_ON",
	"STATE_DUMP",
	"STATE_SHUTDOWN",
	"STATE_FLASHING",
};

bool mid_statemachine_get_event_by_name(const char* name, MID_Event_t* event)
{
	MID_Event_t i;
	for(i=MID_IPC_RBT_EVT; i<=MID_LAST_EVT; i++)
		if(strcmp(name, MID_EventNames[i])==0) {
			*event=i;
			return true;
		}
	return false;
}

// Helper functions

static inline void lock_mid_data(struct mid* mid_info)
{
	apr_status_t status;
	if (unlikely((status = apr_thread_mutex_lock(mid_info->mutex)) != APR_SUCCESS)) {
		char message[100];
		MLGE("MID: MID Unable to take internal mid data mutex: %s",
			apr_strerror(status, message, sizeof(message)));
	}
}

static inline void unlock_mid_data(struct mid* mid_info)
{
	apr_status_t status;
	if (unlikely((status = apr_thread_mutex_unlock(mid_info->mutex)) != APR_SUCCESS)) {
		char message[100];
		MLGE("MID: MID Unable to release internal mid data mutex: %s",
			apr_strerror(status, message, sizeof(message)));
	}
}

#define LOCK_MID_DATA	lock_mid_data(mid_info)
#define UNLOCK_MID_DATA	unlock_mid_data(mid_info)

static char * mid_fw_upgrade_get_error_str(struct mid* mid_info, int error_code)
{
	char * ret = NULL;
	int i;

	unsigned char num_codes
	    = sizeof(mid_fw_upgr_errors) / sizeof(mid_fw_upgr_error_code_t);

	for (i = 0; i < num_codes; i++) {
		if (mid_fw_upgr_errors[i].error_code == error_code)
			break;
	}

	if (i < num_codes)
		ret = mid_fw_upgr_errors[i].str;
	else
		ret = mid_info->moli_fw_upgr_get_error_str(mid_info, error_code);

	return ret;
}

static const char* get_reason_from_last_event(struct mid* mid_info)
{
	switch(mid_info->cur_event) {
		case MID_IPC_RBT_EVT: return "ipc";
		case MID_GPIO_RST2_EVT: return "rst2";
		case MID_GPIO_PRST_EVT: return "prst";
		case MID_MODEM_HALT_EVT: return "modem_halt";
		case MID_MODEM_START_EVT: return "modem_start";
		case MID_SIG_KILL_EVT: return "kill";
		case MID_LONG_OPS_EVT: return "long_op";
		case MID_CFRDY_EVT: return "caif_ready";
		case MID_HIGH_TEMPERATURE_EVT: return "temp";
		case MID_LOW_BATTERY_EVT: return "battery";
		case MID_AT_CFUN0_EVT: return "cfun=0";
		case MID_AT_CFUN100_EVT: return "cfun=100";
		case MID_IPC_FW_UPGR_IMG_XFER_EVT: return "fw_upgrade_xfer";
		case MID_IPC_FW_UPGR_FLASH_EVT: return "fw_upgrade_flash";
		case MID_IPC_FW_UPGR_STATUS_EVT: return "fw_upgrade_status";
		case MID_FW_XFER_OPS_EVT: return "fw_upgrade_op";
		case MID_FW_XFER_PROGRESS_EVT: return "fw_upgrade_progress";
		case MID_TIMEOUT_EVT: return "timeout";
		case MID_FLASH_START_EVT: return "flash_start";
		case MID_FLASH_END_EVT: return "flash_end";
		case MID_LAST_EVT:
		default: return "[unknown]";
	}
}

static int state_all_act_shutdown(struct mid* mid_info, int sendCfun)
{
	int res = 0;
	union ipc_msg msg;
	ATResponse *atresponse = NULL;

	MLGD("STMACH: MID execute Action: Shutdown.");
	/*  Notify other AT client that a shutdown is nearby. */
	ipc_prepare_message(&msg, STATECHANGE, "prepare_off", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);
	MLGD("STMACH: MID sent prepare_off message. A modem shutdown is coming.");

	/* Special case - no PWRRSTIN available, we can use RESOUT2 to check if modem is alive under specific conditions. */
	if (unlikely(!mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2) && mid_gpio_used(mid_info->gpios, GPIO_ID_RESOUT2) && !mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN))) {
		if (!mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN) && mid_info->modem_is_on == 0) {
			MLGD("MID: Modem is already down according GPIO RESOUT2 level - MID will now cut power.");
			goto modem_already_off;
		}
	}

	/* Normal case - PWRRSTIN is used to check if modem is on or off. */
	if (unlikely(!mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN) && mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN))) {
		MLGD("MID: Modem is already down according GPIO PWRRSTIN level - MID will now cut power.");
		goto modem_already_off;
	}

	/* If AT channel is available. Use it to power down with AT+CFUN commands. */
	if ((mid_info->at_chnl_fd >= 0) && (sendCfun)) {
		int *fd = &(mid_info->at_chnl_fd);
		int written = 0;

		MLGD("MID: AT channel available. Try to shutdown with AT commands.");

		/* Case where we got a AT+CFUN=0 requested on another AT channel. A modem reboot is trigged. */
		if (FLAG_UP(mid_info->event, MID_AT_CFUN0_EVT) &&
				mid_info->config->enable_efwd_atcfun) {
			/* Send OK response to command CFUN=0 on other channel. */
			MLGD("AT: >>> Forward command reply for CFUN=: %s", mid_info->at_cfun0_resp);
			written = write(*fd, mid_info->at_cfun0_resp, strlen(mid_info->at_cfun0_resp));
			if (written != (int)strlen(mid_info->at_cfun0_resp)) {
				MLGW("AT: >>> Failure on AT+CFUN  forwarded command.");
			}
			free(mid_info->at_cfun0_resp);
			/* Toggle a reboot event */
			MLGD("STMACH: MID_IPC_RBT_EVT event set");
			SET_FLAG(mid_info->event, MID_IPC_RBT_EVT);
		}

		/* Case for AT+CFUN=100 enabled modem. */
		if (mid_info->prep_for_shutdown) {
			/* AT+CFUN=100 is done if we use a OnSwC modem or flashless configuration. */
			if (mid_info->config->use_on_sw_c || mid_info->config->initial_enable_bootldr) {
				MLGD("AT: >>> Execute AT+CFUN=100.");
				res = at_send_command("AT+CFUN=100", &atresponse);
				if (res < 0 || atresponse->success == 0) {
					MLGW("AT: >>> Failure on AT+CFUN command.");
				}
				at_response_free(atresponse);
			}
			/* Case where we got a AT+CFUN=100 requested on another AT channel. */
			if (FLAG_UP(mid_info->event, MID_AT_CFUN100_EVT) &&
					mid_info->config->enable_efwd_atcfun) {
				/* Send OK response to command CFUN=100 on other channel. */
				MLGD("AT: >>> Forward command reply for CFUN=: %s", mid_info->at_cfun100_resp);
				written = write(*fd, mid_info->at_cfun100_resp, strlen(mid_info->at_cfun100_resp));
				if (written != (int)strlen(mid_info->at_cfun100_resp)) {
					MLGW("AT: >>> Failure on AT+CFUN  forwarded command.");
				}
				free(mid_info->at_cfun100_resp);
			}
			/* If we force it, AT+CFUN=0 is used to shutdown. Typical case is OnSwA modem. */
			if (mid_info->config->use_atcfun0) {
				MLGD("AT: >>> Execute AT+CFUN=0.");
				res = at_send_command("AT+CFUN=0", &atresponse);
				if (res < 0 || atresponse->success == 0) {
					MLGW("AT: >>> Failure on AT+CFUN command.");
				}
				at_response_free(atresponse);
			}
		} else {
		/* Case for non AT+CFUN=100 enabled modem. */
			MLGD("AT: >>> Execute AT+CFUN=0.");
			res = at_send_command("AT+CFUN=0", &atresponse);
			if (res < 0 || atresponse->success == 0) {
				MLGW("AT: >>> Failure on AT+CFUN command.");
			}
			at_response_free(atresponse);
		}
	} else {
		/* Modem is on, but we don t have AT channel, so we need to force power off via GPIO */
		res = 1;
	}

modem_already_off:
	if ((mid_info->config->enable_at_channel) && (mid_info->at_chnl_fd >= 0))
		mid_at_chnl_close(mid_info);

	return res;
}

int state_all_evt_sig_kill(struct mid* mid_info)
{
	int res = 0;
	apr_status_t status;
	MLGD("STMACH: MID execute Action: SIG_KILL processing.");

	CLEAR_FLAG(mid_info->event, MID_SIG_KILL_EVT);
	MLGD("STMACH: MID_SIG_KILL_EVT event cleared.");

	if (mid_info->config->enable_at_channel && (mid_info->at_chnl_fd >= 0))
		mid_at_chnl_close(mid_info);

	mid_enable_gpio_out_safe_mode(mid_info);
	if(mid_info->gpios) mid_gpio_release_all(mid_info->gpios);
	/* Disconnect IPC */
	res = ipc_medium_disconnect();
	if (unlikely(res != 0))
		MLGE("MID: Unable to stop IPC");
#ifdef DEBUG_TRACE
	if (unlikely(mid_dbg_info.log_fd != NULL)) {
		res = fclose(mid_dbg_info.log_fd);
		mid_dbg_info.log_fd = NULL;
		if (res)
			MLGE("MID: Unable to close LOG file");
	}
#endif

	/* TODO check how to clean up - If used by config, after a destroy, the value is not 0 or NULL
	if used by command line, a malloc is done
	if (mid_info->cfgfile != NULL)
		free(mid_info->cfgfile);
	if (mid_info->rdir != NULL)
		free(mid_info->rdir);
	*/

	if (mid_info->dl_handle != NULL)
		dlclose(mid_info->dl_handle);

	/* Clean up everything */
	status = apr_thread_mutex_destroy(mid_info->mutex);
	if (unlikely(status != APR_SUCCESS)) {
		char message[100];
		MLGE("MID: Unable to destroy internal mid data mutex: %s",
			apr_strerror(status, message, sizeof(message)));
	}

	MLGD("MID: MID ended.");
	exit(EXIT_SUCCESS);
}

static int modem_power_off(struct mid* mid_info)
{
	int res = 0;

	MLGD("STMACH: MID execute power OFF sequence");
	/* Have to put IO in safe mode before power off in order to prevent leakage */
	mid_enable_io_safe_mode(mid_info);

	if (mid_info->config->use_on_sw_c) {
		/* OnSwC */
		MLGD("GPIO: Using OnSwC");
		MLGD("GPIO: De-assert MOD ON pin");
		res = mid_gpio_deassert(mid_info->gpios, GPIO_ID_ON);
		if (unlikely(res)) {
			MLGE("GPIO: Can't deassert GPIO_ON");
			return -EMIDGPIO;
		}
	} else {
		/* OnSwA */
		MLGD("GPIO: Using OnSwA");
		MLGD("GPIO: Assert MOD ON pin");
		res = mid_gpio_assert(mid_info->gpios, GPIO_ID_ON);
		if (unlikely(res)) {
			MLGE("GPIO: Can't assert GPIO_ON");
			return -EMIDGPIO;
		}
		MLGD("GPIO: OnSwa long assert: %d seconds.", MODEM_POWER_OFF_LAT);
		sleep(MODEM_POWER_OFF_LAT);
		MLGD("GPIO: De-assert MOD ON pin");
		res = mid_gpio_deassert(mid_info->gpios, GPIO_ID_ON);
		if (unlikely(res)) {
			MLGE("GPIO: Can't deassert GPIO_ON");
			return -EMIDGPIO;
		}
	}

	MLGD("GPIO: De-assert PWRRSTOUT pin");
	usleep(2000);
	if (unlikely(mid_gpio_deassert(mid_info->gpios, GPIO_ID_PWRRSTOUT))) {
		MLGE("GPIO: Can't deassert GPIO_PWRRSTOUT");
		return -EMIDGPIO;
	}
	MLGD("STMACH: MID will be idle for 50 millisecond to allow safe mode propagation on IOs.");
	usleep(50000);
	return 0;
}

void shutdown_modem(struct mid* mid_info)
{
	int ko_shutdown_result = 0;
	ssize_t maxwaittime = 100; /* Number of iteration in 50msec unit. Total is 50*100 = 5 sec */
	ssize_t reloadmaxwaittime = 20; /* Releoad timeout for 1 second on wrong shutdown operation */
	union ipc_msg msg;

	MLGD("STMACH: MID execute shutdown modem sequence");
	/* Signal all utility threads to shut down */
	mid_info->kill_threads = 1;

	/* Shutdown during fw upgrade flashing */
	/* If we fore some reason hasn't answered the IPCDB request for flashing we send a response now. */
	if (mid_info->fw_upgr_flash_reply_msg) {
		const char *errorstr = mid_fw_upgrade_get_error_str(mid_info,mid_info->fw_upgr_last_error);
		if (ipc_send_async_reply(&mid_info->fw_upgr_flash_reply_msg,
								 (errorstr ? errorstr : "ERROR")))
			MLGE("MID: Failed to send fw upgrade error reply");
	}

	ko_shutdown_result = state_all_act_shutdown(mid_info, 1);
	/* After this point, AT channel is closed or not available */

	if (ko_shutdown_result)
		MLGW("MID: AT command not available to shutdown.  Modem will be killed via GPIO control.");

	/* Time to execute Off command. If we do this after powering off we will have leakage. */
	MLGD("Cleaning up drivers before powering off in order to prevent leakage when power off modem");
	(void)my_system(mid_info, mid_info->config->mod_off_cmd);

	/* Power off the modem via ONSWa or ONSWc now */
	/* To poweroff the following condition should apply :
	 * - GPIO RESOUT2 or GPIO PWRRSTIN used and high
	 * - Prepare for shutdown enabled modem (AT+CFUN=100) and no forced AT+CFUN=0 shutdown command
	 *   OR
	 * - Error on AT commands (ko_shutdown_result condition)
	 *
	 * For forced AT+CFUN=0 shutdown command (use_atcfun=0 condition) or "non prepare for shutdown" enabled modem
	 * the standard way to shutdown is AT+CFUN=0. The modem will turn off by itself.
	 */
	if (((mid_info->prep_for_shutdown && !mid_info->config->use_atcfun0) || ko_shutdown_result)
		&& ((mid_gpio_used(mid_info->gpios, GPIO_ID_RESOUT2) && mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2))
			|| (mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN) && mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN))))
		if (modem_power_off(mid_info))
			MLGE("STMACH: Trouble in power off sequence. Modem may not be switched Off.");

	/* Last paranoiac check - PWRRSTIN & RESOUT2 should be low for prepare_for_shutdown enabled modem */
	if (unlikely(mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2) || mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN))) {
		/* If they don't MID will report error */
		if (mid_info->prep_for_shutdown && !mid_info->config->use_atcfun0) {
			if (mid_info->config->modem_arch_id == OSMIUM) {
				/* An insane case, DBB is powered off but ABB not. */
				if (!(mid_gpio_used(mid_info->gpios, GPIO_ID_RESET) && (mid_gpio_assert(mid_info->gpios, GPIO_ID_RESET)))) {
					MLGE("STMACH: Modem is not powered off after GPIO_RESET sequence. This is a MODEM bug - MID will be out of sync.");
#if defined(MOTOROLA_FEATURE)
					mid_statemachine_log_panic_apr("Subtype: Modem is not powered off after GPIO_RESET sequence.\n", mid_info->config->critical_panic_detection_file);
#endif
					mid_platform_reboot(mid_info);
				}
			}
			else {
				MLGE("STMACH: Modem is not powered off after GPIO_ON sequence. This is a MODEM bug - MID will be out of sync.");
#if defined(MOTOROLA_FEATURE)
				mid_statemachine_log_panic_apr("Subtype: Modem is not powered off after GPIO_ON sequence.\n", mid_info->config->critical_panic_detection_file);
#endif
				mid_platform_reboot(mid_info);
			}
		}
	}
	/* Special case to call a GPIO forced power off */
	if (mid_info->config->force_gpio_pwroff)
		modem_power_off(mid_info);

	/* Active loop to wait modem shutdown completion. Modem should be already off here, except if AT+CFUN=0 command is used with success
	 * Then we let 5 sec to modem to complete power down.
	 */
	while ((mid_gpio_used(mid_info->gpios, GPIO_ID_RESOUT2) && mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2))
		|| (mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN) && mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN))) {
		/* Sleep 50 milliseconds */
		usleep(50000); /* Poll every 50msec */
		maxwaittime--;
		if (maxwaittime == 0) {
			MLGE("STMACH: Modem shutdown timeout.");
			if (mid_info->config->modem_arch_id == OSMIUM) {
				/* An insane case, DBB is powered off but ABB not. */
				if (!(mid_gpio_used(mid_info->gpios, GPIO_ID_RESET) && (mid_gpio_assert(mid_info->gpios, GPIO_ID_RESET)))) {
					MLGE("STMACH: Modem is not powered off after GPIO_RESET sequence. This is a MODEM bug - MID will be out of sync.");
					MLGE("GPIO: GPIO_ID_RESOUT2 STATUS :%d",  mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2));
					MLGE("GPIO: GPIO_ID_PWRRSTIN STATUS :%d",  mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN));
#if defined(MOTOROLA_FEATURE)
					mid_statemachine_log_panic_apr("Subtype: Modem is not powered off after GPIO_RESET sequence and shutdown timeout.\n", mid_info->config->critical_panic_detection_file);
#endif
					mid_platform_reboot(mid_info);
				}
				if (reloadmaxwaittime) {
					maxwaittime = reloadmaxwaittime;
					reloadmaxwaittime = 0;
				}
				else {
					MLGE("STMACH: MID is NOW out of sync. MID is in STATE_OFF BUT modem is in Zombie state.");
					MLGE("GPIO: GPIO_ID_RESOUT2 STATUS :%d",  mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2));
					MLGE("GPIO: GPIO_ID_PWRRSTIN STATUS :%d",  mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN));
					/* Unrecoverable case: This will trig a platform reboot or a MID spin loop. */
#if defined(MOTOROLA_FEATURE)
					mid_statemachine_log_panic_apr("Subtype: MID is NOW out of sync. MID is in STATE_OFF BUT modem is in Zombie state after shutdown timeout.\n", mid_info->config->critical_panic_detection_file);
#endif
					mid_platform_reboot(mid_info);
				}
			}
			else {
				MLGE("STMACH: MID is NOW out of sync. MID is in STATE_OFF BUT modem is in Zombie state.");
				MLGE("GPIO: GPIO_ID_RESOUT2 STATUS :%d",  mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2));
				MLGE("GPIO: GPIO_ID_PWRRSTIN STATUS :%d",  mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN));
				if (!mid_info->shutdown_retry_left) {
					/* Unrecoverable case: This will trig a platform reboot or a MID spin loop. */
#if defined(MOTOROLA_FEATURE)
					mid_statemachine_log_panic_apr("Subtype: MID is NOW out of sync. MID is in STATE_OFF BUT modem is in Zombie state after shutdown timeout.\n", mid_info->config->critical_panic_detection_file);
#endif
					mid_platform_reboot(mid_info);
				} else {
					/* On rare occasions this can be recoverable... */
					mid_info->shutdown_retry_left--;
					return;
				}
			}
		}
	}

	/* OSMIUM special case, deassert GPIO_ID_RESET if used. */
	if (mid_info->config->modem_arch_id == OSMIUM && mid_gpio_used(mid_info->gpios, GPIO_ID_RESET) && mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESET))
		mid_gpio_deassert(mid_info->gpios, GPIO_ID_RESET);

	MLGD("STMACH: Modem is now off.");

	if (mid_info->ipc_reboot_received) {
		mid_info->ipc_reboot_received = 0;
#if defined(MOTOROLA_FEATURE)
		mid_statemachine_log_panic_apr("Subtype: Modem IPC failure, no system reboot.\n", mid_info->config->silent_panic_detection_file);
#endif
	}

	ipc_prepare_message(&msg, STATECHANGE, "off", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);
	mid_info->state = STATE_OFF;
	mid_info->shutdown_retry_left = mid_info->config->shutdown_retry;
}

static int modem_power_on(struct mid* mid_info, int disable_io_safe_mode)
{
	int res = 0;
	MLGD("STMACH: MID execute power on sequence");
#if !defined(MOTOROLA_FEATURE)
	if (disable_io_safe_mode)
#endif
		mid_disable_io_safe_mode(mid_info);
	MLGD("GPIO: Assert PWRRSTOUT pin");
	if (unlikely(mid_gpio_assert(mid_info->gpios, GPIO_ID_PWRRSTOUT))) {
		MLGE("GPIO: Can't assert GPIO_PWRRSTOUT");
		mid_enable_io_safe_mode(mid_info);
		return -EMIDGPIO;
	}
	usleep(2000);

	if (mid_info->config->use_on_sw_c) {
		/* OnSwC */
		MLGD("GPIO: Using OnSwC");
		MLGD("GPIO: Assert MOD ON pin");
		res = mid_gpio_assert(mid_info->gpios, GPIO_ID_ON);
		if (unlikely(res)) {
			MLGE("GPIO: Can't assert GPIO_ON");
			mid_enable_io_safe_mode(mid_info);
			return -EMIDGPIO;
		}
		if (!mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN))
			mid_info->modem_is_on = 1;

		return 0;
	}

	/* OnSwA */
	MLGD("GPIO: Using OnSwA");
	MLGD("GPIO: Assert MOD ON pin");
	res = mid_gpio_assert(mid_info->gpios, GPIO_ID_ON);
	if (unlikely(res)) {
		MLGE("GPIO: Can't assert GPIO_ON");
		mid_enable_io_safe_mode(mid_info);
		return -EMIDGPIO;
	}
	mid_info->power_on_release = 1;
	if ((!isdirempty(mid_info->config->initial_rdir) && (!mid_info->config->initial_enable_bootldr)) || !mid_gpio_used(mid_info->gpios, GPIO_ID_RESOUT2)) {
		/* This is for:
		 * - non RESOUT2 enabled hardware
		 * - flashed configuration when modem has to be flashed - No RESOUT2 are available from loaders */
		MLGD("GPIO: OnSwa long assert: %d seconds.", MODEM_POWER_ON_LAT);
		sleep(MODEM_POWER_ON_LAT);
		MLGD("GPIO: De-assert MOD ON pin");
		res = mid_gpio_deassert(mid_info->gpios, GPIO_ID_ON);
		if (unlikely(res)) {
			MLGE("GPIO: Can't deassert GPIO_ON");
			mid_info->power_on_release = 0;
			mid_enable_io_safe_mode(mid_info);
			return -EMIDGPIO;
		}
		mid_info->power_on_release = 0;
	}
	if (!mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN))
		mid_info->modem_is_on = 1;
	return 0;
}

// Helper thread procedures

struct delayed_dump_args {
	struct mid* mid_info;
	unsigned int seconds;
};
static void *delayed_dump(apr_thread_t* thread, void *arg)
{
	UNUSED(thread);
	struct delayed_dump_args* args=arg;
	MLGD("STMACH: MID execute delayed dump sequence, with %d seconds delay", args->seconds);
	sleep(args->seconds);
	SET_FLAG(args->mid_info->event, MID_GPIO_RST2_EVT);
	MLGD("STMACH: MID_GPIO_RST2_EVT event set.");
	free(args);
	return NULL;
}
static int start_delayed_dump(apr_thread_t** thread, struct mid* mid_info, unsigned int seconds)
{
	struct delayed_dump_args* args=malloc(sizeof(struct delayed_dump_args));
	args->mid_info=mid_info;
	args->seconds=seconds;
	return apr_thread_create(thread, NULL, delayed_dump, args, mid_info->pool);
}

static void *rfm_sleep(apr_thread_t* thread, void *arg)
{
	UNUSED(thread);
	struct mid* mid_info=arg;
	int timetosleep = mid_info->config->rfm_delay;
	UNUSED(arg);
	MLGD("STMACH: SIMULATED MID_CFRDY_EVT (with a sleep command with sleep delay: %d) \n", timetosleep);
	if (mid_info->config->initial_sec_data_conf == 1) {
		/* Sanity delay to let enough time for a non prepared hardware */
		timetosleep = 10;
	}
	sleep(timetosleep);
	if (mid_info->config->enable_at_channel) {
		if (mid_at_chnl_open(mid_info)) {
			MLGE("MID: Unable to open AT channel. Modem will be shutdown.");
			shutdown_modem(mid_info);
			return 0;
		}
		/* Response for oppening the AT channel will generated the CFRDY event */
		return 0;
	}
	/* Since we don't open the AT channel we must generate the CFRDY event here */
	SET_FLAG(mid_info->event, MID_CFRDY_EVT);
	MLGD("STMACH: MID_CFRDY_EVT event set.");
	return 0;
}

/***************
ANY
***************/
MID_Event_Oper_t MID_State_Any__Event_GPIO_PRST_EVT(struct mid *mid_info)
{
	union ipc_msg msg;
	MLGD("STMACH: MID execute Action: GPIO_PRST_EVT processing.");

	if (mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN)) {
		MLGW("STMACH: Modem GPIO PWRRSTIN event, and GPIO level is high. This means modem is powered.");
		return MID_CLEAR_EVENT;
	}
	MLGW("STMACH: Modem unexpected powered off according GPIO PWRRSTIN.");
	(void)my_system(mid_info, mid_info->config->mod_off_cmd);
	ipc_prepare_message(&msg, STATECHANGE, "off", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);
	mid_info->state = STATE_OFF;
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_Any__Event_HIGH_TEMPERATURE_EVT(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: HIGH_TEMPERATURE_EVT processing.");
	shutdown_modem(mid_info);
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_Any__Event_LOW_BATTERY_EVT(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: LOW_BATTERY_EVT processing.");
	shutdown_modem(mid_info);
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t UnExpected_Event(struct mid *mid_info)
{
	MLGE("STMACH: MID received event:%s in state:%s. This event will not be handled and cleared because it s a out of sync event.", MID_EventNames[mid_info->cur_event],MID_StateNames[mid_info->state]);
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t Save_Event(struct mid *mid_info_p)
{
	UNUSED(mid_info_p);
	return MID_KEEP_EVENT;
}

MID_Event_Oper_t MID_State_Any__Event_SIG_KILL(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: SIG_KILL processing.");
	shutdown_modem(mid_info);
	return MID_KEEP_EVENT;
}

MID_Event_Oper_t UnExpected_Async_Event(struct mid *mid_info)
{
	MLGE("STMACH: MID received event:%s in state:%s. This event will not be handled and cleared because it s a out of sync event.", MID_EventNames[mid_info->cur_event],MID_StateNames[mid_info->state]);
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_Any__Event_FW_XFER_OPS(struct mid *mid_info)
{
	const char * errorstr = NULL;
	MLGD("STMACH: MID execute Action: FW_XFER_OPS_EVT processing.");
	MLGD("STMACH: MID_State_Any__Event_FW_XFER_OPS - result is %d", mid_info->fw_xfer_result);

	/* Handler for fw xfer thread status event */

	if (mid_info->fw_xfer_result == MID_FW_XFER_OK) {
		MLGD("MID: MID_State_Any__Event_FW_XFER_OPS: Got OK");    //DBG
		if (ipc_send_async_reply(&mid_info->fw_upgr_img_xfer_reply_msg, "OK")) {
			MLGE("DBUS: MID_State_Any__Event_FW_XFER_OPS: Failed to send reply");
		}

	} else {
		MLGD("MID: MID_State_Any__Event_FW_XFER_OPS: Got ERROR"); //DBG
		errorstr = mid_fw_upgrade_get_error_str(mid_info, mid_info->fw_upgr_last_error);
		if (ipc_send_async_reply(&mid_info->fw_upgr_img_xfer_reply_msg,
			   (errorstr ? errorstr : "ERROR"))) {
			MLGE("DBUS: MID_State_Any__Event_FW_XFER_OPS: Failed to send reply");
		}
	}
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_Any__Event_FW_UPGR(struct mid *mid_info)
{
	const char * error = NULL;
	MLGD("STMACH: MID execute Action: FW_UPGR_EVT processing.");

	switch (mid_info->cur_event) {
	case MID_IPC_FW_UPGR_IMG_XFER_EVT:
		if (mid_info->fw_upgr_img_xfer_reply_msg != NULL) {
			error = mid_fw_upgrade_get_error_str(mid_info, MID_FW_UPGR_RSLT_REJECTED);
			if (ipc_send_async_reply(&mid_info->fw_upgr_img_xfer_reply_msg,
						 (error ? error : "REJECTED"))) {
				MLGE("DBUS: UnExpected_Async_Event: Failed to send reply");
			}
		}
		break;
	case MID_IPC_FW_UPGR_FLASH_EVT:
		if (mid_info->fw_upgr_flash_reply_msg != NULL) {
			error = mid_fw_upgrade_get_error_str(mid_info, MID_FW_UPGR_RSLT_REJECTED);
			if (ipc_send_async_reply(&mid_info->fw_upgr_flash_reply_msg,
						 (error ? error : "REJECTED"))) {
				MLGE("DBUS: UnExpected_Async_Event: Failed to send reply");
			}
		}
		break;
	case MID_IPC_FW_UPGR_STATUS_EVT:
		if (mid_info->fw_upgr_status_reply_msg != NULL) {
			error = mid_fw_upgrade_get_error_str(mid_info, MID_FW_UPGR_RSLT_REJECTED);
			if (ipc_send_async_reply(&mid_info->fw_upgr_status_reply_msg,
						 (error ? error : "REJECTED"))) {
				MLGE("DBUS: UnExpected_Async_Event: Failed to send reply");
			}
		}
		break;
	case MID_IPC_RBT_EVT: /* Fall though */
	case MID_GPIO_RST2_EVT: /* Fall though */
	case MID_GPIO_PRST_EVT: /* Fall though */
	case MID_MODEM_HALT_EVT: /* Fall though */
	case MID_MODEM_START_EVT: /* Fall though */
	case MID_SIG_KILL_EVT: /* Fall though */
	case MID_LONG_OPS_EVT: /* Fall though */
	case MID_CFRDY_EVT: /* Fall though */
	case MID_HIGH_TEMPERATURE_EVT: /* Fall though */
	case MID_LOW_BATTERY_EVT: /* Fall though */
	case MID_AT_CFUN0_EVT: /* Fall though */
	case MID_AT_CFUN100_EVT: /* Fall though */
	case MID_FW_XFER_OPS_EVT: /* Fall though */
	case MID_FW_XFER_PROGRESS_EVT: /* Fall though */
	case MID_TIMEOUT_EVT: /* Fall though */
	case MID_FLASH_START_EVT: /* Fall though */
	case MID_FLASH_END_EVT: /* Fall though */
	case MID_LAST_EVT: /* Fall though */
	default:
		MLGE("STMACH: Current event(%s) doesn't match any firmware upgrade event!",MID_EventNames[mid_info->cur_event]);
		break;
	}
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_Any__Event_TIMEOUT(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: TIMEOUT_EVT processing.");
	MLGW("STMACH: Shutting down modem due to timeout in a transient state");

	/* If none of the two events below is set we need to set these in */
	/* order to reboot after shutting down modem */
	if (!FLAG_UP(mid_info->event, MID_MODEM_START_EVT) && !FLAG_UP(mid_info->event, MID_IPC_RBT_EVT)) {
		SET_FLAG(mid_info->event, MID_MODEM_START_EVT);
		MLGD("STMACH: MID_MODEM_START_EVT event set.");
	}
	/* Special care when we are in ModInfUp state and when we don t receive EMRDY from modem. AT channel could be non-responsive
	 * So AT is closed here to prevent shutdown hanging */
	if (mid_info->state == STATE_MODINF_UP)
		mid_at_chnl_close(mid_info);

	shutdown_modem(mid_info);
	return MID_CLEAR_EVENT;
}

/***************
FLASHING
***************/

static MID_Event_Oper_t MID_State_Flashing__Event_GPIO_PRST(struct mid *mid_info)
{
	if (mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN) == 0) {
		MLGD("Ingnoring PWRRSTIN going low during flashing");
		return MID_CLEAR_EVENT;
	}
	mid_disable_tx_safe_mode(mid_info);
	mid_mfa_notify_on(mid_info->mfa);
	return MID_CLEAR_EVENT;
}

static MID_Event_Oper_t MID_State_Flashing__Event_FLASH_START(struct mid *mid_info)
{
	if (modem_power_on(mid_info, 1) != 0) {
		MLGE("MFA: Failed to power up the modem");
		mid_mfa_stop(mid_info->mfa, false);
		SET_FLAG(mid_info->event, MID_FLASH_END_EVT);
	}
	return MID_CLEAR_EVENT;
}

static MID_Event_Oper_t mid_statemachine_try_boot(struct mid *mid_info)
{
	union ipc_msg msg;
#if defined(MOTOROLA_FEATURE)
	if (mot_mid_info.dual_boot_mode)
		mid_info->boot_retry_left = mid_info->config->boot_retry;
#endif
	/* FLASHED VARIANT - POWER UP MODEM */
	MLGD("MID: MID power up the modem started (Retries left:%d)",mid_info->boot_retry_left);

	if (mid_info->boot_retry_left == 0 && !mid_info->boot_failed) {
		if (mid_info->config->reboot_on_failure)
#if defined(MOTOROLA_FEATURE)
		{
			mid_statemachine_log_panic_apr("Subtype: MID bootloader: maximum number of retries reached.\n", mid_info->config->critical_panic_detection_file);
#endif
			mid_platform_reboot(mid_info);
#if defined(MOTOROLA_FEATURE)
		}
#endif
		MLGD("MID: Maximum(%d) number of retries reached", mid_info->config->boot_retry);
		MLGD("STMACH: MID will stay in STATE_OFF.");
		mid_info->boot_retry_left = mid_info->config->boot_retry;
		mid_info->boot_failed = 1;
		return MID_CLEAR_EVENT;
	} else if (mid_info->boot_retry_left > 0) {
		mid_info->boot_failed = 0;
	}

	mid_info->boot_retry_left--;

#ifdef PERF_MEASUREMENT
	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemImagesStarted))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");

	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemImagesFinished))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");

#endif

	if (modem_power_on(mid_info, 0)) {
		MLGE("GPIO: MID modem power on failure");
		return MID_KEEP_EVENT;
	}
	if (!mid_gpio_used(mid_info->gpios, GPIO_ID_RESOUT2) || (!mid_gpio_is_ctrl_enabled(mid_info->gpios))) {
		/* Flashed variant witout gpio RST2 ctrl - e.g. PC start everything here */
		if (my_system(mid_info, mid_info->config->mod_ifup_cmd) != 0) {
			/* We always check for a modem dump in shutdown_modem */
			/* unlesss we tried retrieveing a dump before we call shutdown */
			shutdown_modem(mid_info);
			return MID_KEEP_EVENT;
		}

		if (mid_info->config->enable_at_channel && mid_at_chnl_open(mid_info)) {
			MLGE("AT: Unable to open AT channel. MID will shutdown the modem.");
			/* We always check for a modem dump in shutdown_modem */
			/* unlesss we tried retrieveing a dump before we call shutdown */
			shutdown_modem(mid_info);
			return MID_KEEP_EVENT;
		}
		/* And we bypass STATE_BOOT as RESOUT2 will never come */
		if (mid_info->config->enable_at_channel)
			/* AT channel enabled, we wait for EMRDY */
			mid_info->state = STATE_MODINF_UP;
		else {
			int res = my_system(mid_info, mid_info->config->mod_on_cmd);
			if (res != 0) {
				/* We always check for a modem dump in shutdown_modem */
				/* unlesss we tried retrieveing a dump before we call shutdown */
				shutdown_modem(mid_info);
				return MID_KEEP_EVENT;
			}
			/* Go in ON - clear START_EVT and RBT_EVT */
			CLEAR_FLAG(mid_info->event, MID_MODEM_START_EVT);
			CLEAR_FLAG(mid_info->event, MID_IPC_RBT_EVT);
			mid_info->state = STATE_ON;
			ipc_prepare_message(&msg, STATECHANGE, "on", get_reason_from_last_event(mid_info));
			ipc_send_message(msg);
			mid_mfa_notify_on(mid_info->mfa);
		}
		return MID_KEEP_EVENT;
	}
	mid_info->state = STATE_BOOT;
	ipc_prepare_message(&msg, STATECHANGE, "booting", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);
	return MID_KEEP_EVENT;
}

static bool mid_statemachine_start_flashing(struct mid* mid_info)
{
	MLGD("MID: %s is not empty. MID flash service started(Retries left:%d)", mid_info->config->initial_rdir, mid_info->flash_retry_left);
	if (mid_info->flash_retry_left == 0 && !mid_info->flash_failed) {
		MLGD("MID: Maximum(%d) number of retries reached, trying to boot modem anyway", mid_info->config->flash_retry);
		mid_info->flash_retry_left = mid_info->config->flash_retry;
		mid_info->flash_failed = true;
		/* The flashing failed n times, lets atleast try to boot modem */
		return false;
	} else if (mid_info->flash_retry_left > 0) {
		mid_info->flash_failed = false;
	}

	mid_info->flash_retry_left--;

#ifdef PERF_MEASUREMENT
	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemImagesStarted))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");
#endif

	mid_disable_gpio_service_safe_mode(mid_info);
	mid_enable_tx_safe_mode(mid_info);

	if (unlikely(mid_gpio_assert(mid_info->gpios, GPIO_ID_SERVICE))) {
		MLGE("GPIO: Can't assert GPIO_MOD_SERVICE");
		mid_enable_gpio_service_safe_mode(mid_info);
		return false;
	}

	if (my_system(mid_info, mid_info->config->mod_prefl_cmd) != 0) {
		if (unlikely(mid_gpio_deassert(mid_info->gpios, GPIO_ID_SERVICE)))
			MLGE("GPIO: Can't deassert GPIO_MOD_SERVICE");
		mid_enable_gpio_service_safe_mode(mid_info);
		/* Ignoring return values when cleaning up */
		(void)my_system(mid_info, mid_info->config->mod_postfl_cmd);
		return false;
	}

	if (mid_mfa_start(mid_info->mfa)) {
		MLGD("MFA started");
	} else {
		MLGE("MID: No dir cleanup will be performed - flash sequence will be again triggered");
		if (unlikely(mid_gpio_deassert(mid_info->gpios, GPIO_ID_SERVICE)))
			MLGE("GPIO: Can't deassert GPIO_MOD_SERVICE");
		mid_enable_gpio_service_safe_mode(mid_info);
		(void)my_system(mid_info, mid_info->config->mod_postfl_cmd);
		shutdown_modem(mid_info);
		return false;
	}

	return true;
}

static MID_Event_Oper_t MID_State_Flashing__Event_FLASH_END(struct mid *mid_info)
{
	MLGD("MID: Modem flash ended.");
	if (!mid_mfa_was_successful(mid_info->mfa)) {
		MLGW("STMACH: MFA Flashing failed. Retrying");

		if (modem_power_off(mid_info) != 0) {
			MLGE("MFA: MID modem power off failure");
		}
#ifdef MOTOROLA_FEATURE
		if (mot_mid_info.recovery_mode) {
			/* Broadcast status if MID was not terminated externally */
			if (!(mid_info->event & 1<<MID_SIG_KILL_EVT)) {
				union ipc_msg msg;
				ipc_prepare_message(&msg, MFARESULT, "fail");
				ipc_send_message(msg);
			}
			/* Exit to recovery executables to potentially retry flashing */
			MLGE("Flash fail: Exiting MID in recovery mode.");
			exit(EXIT_FAILURE);
		}
#endif /* MOTOROLA_FEATURE */

		if (!mid_statemachine_start_flashing(mid_info)) {
			MLGE("MFA: Failed to start flashing");
		}

		// If we've given up flashing, we just continue with normal bootup.
		if (!mid_info->flash_failed) {
			return MID_CLEAR_EVENT;
		}
	}

	if(purgedir(mid_info->config->initial_rdir))
		MLGE("MID: Failed to purge dir: %s. Continue anyway", mid_info->config->initial_rdir);

#ifdef MOTOROLA_FEATURE
	if (!mot_mid_info.recovery_mode)
	{
#endif
	/* Since the modem images files is deleted we keep the event */
	/* so the modem will be tried to boot(without flashing) in  */
	/* the next iteration */
	if (my_system(mid_info, mid_info->config->mod_postfl_cmd) != 0)
		return MID_KEEP_EVENT;
#ifdef MOTOROLA_FEATURE
	}
	else
	{
		/* MID was launched in recovery mode: shutdown modem */
		my_system(mid_info, mid_info->config->mod_postfl_cmd);
		shutdown_modem(mid_info);

		/* Broadcast status if MID was not terminated externally */
		if (!(mid_info->event & 1<<MID_SIG_KILL_EVT)) {
			union ipc_msg msg;
			ipc_prepare_message(&msg, MFARESULT, "pass");
			ipc_send_message(msg);
		}
		/* Exit to recovery executables to reboot to normal mode. */
		MLGE("Flash success: Exiting MID in recovery mode.");
		exit(EXIT_SUCCESS);
	}
#endif /* MOTOROLA_FEATURE */

	/* Since flashing went well we just continue even if this failed */
	/* Modem is turned off by MFA. This may not be correct */
	if (unlikely(mid_gpio_deassert(mid_info->gpios, GPIO_ID_SERVICE)))
		MLGE("GPIO: Can't deassert GPIO_MOD_SERVICE");

	if (unlikely(mid_gpio_deassert(mid_info->gpios, GPIO_ID_PWRRSTOUT)))
		MLGE("GPIO: Can't deassert GPIO_PWRRSTOUT");

	/* Set all interface in safe mode */
	mid_enable_gpio_service_safe_mode(mid_info);
	mid_enable_io_safe_mode(mid_info);

	/* Sanity sleep to let time for HW */
	MLGD("STMACH: MID will be idle for 50 millisecond to allow safe mode propagation on IOs.");
	usleep(50000);

	/* Check if modem is down - otherwise power it off */
	if (mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2) || mid_gpio_get_value(mid_info->gpios, GPIO_ID_PWRRSTIN)
			|| mid_info->config->flash_force_pwroff) {
		if (mid_info->config->flash_force_pwroff)
			/* For modem that does not automaticaly power off, we can force it */
			MLGD("STMACH: Option flash_force_pwroff is set. Modem power_off function is called now.");
		else
			MLGD("STMACH: Modem is ON according RESOUT2 and / or PWRRSTIN level. Modem power_off function is called now.");
		shutdown_modem(mid_info);
	}

	return mid_statemachine_try_boot(mid_info);
}

/***************
OFF
***************/

MID_Event_Oper_t MID_State_Off__Event_SIG_KILL(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: SIG_KILL_EVT processing.");
	state_all_evt_sig_kill(mid_info);
	return MID_KEEP_EVENT;
}

/* Only used in the function below. We cannot use stack variable when creating thread. */
/* The stack is changed before the thread use the variable */
static int _timeout;

MID_Event_Oper_t MID_State_Off__Event_MODEM_START__IPC_RBT(struct mid *mid_info)
{
	int res;
	apr_status_t status;
	MLGD("STMACH: MID execute Action: START / IPC_RBT_EVT processing.");

	if (FLAG_UP(mid_info->event, MID_MODEM_HALT_EVT)) {
		CLEAR_FLAG(mid_info->event, MID_MODEM_HALT_EVT);
		MLGD("STMACH: MID_MODEM_HALT_EVT event cleared.");
		return MID_CLEAR_EVENT;
	}

	mid_info->kill_threads = 0;

	/* Since the current setup doesn't always support RSTIN we need to keep modem on state */
	if (!mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN))
		mid_info->modem_is_on = 0;

#ifdef PERF_MEASUREMENT
	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemBootStarted))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");
#endif

	/* TODO: Consider remove this when crashdump is tested and working */
	if (mid_info->config->crashdump_dev_mode == -1) {
		res = start_delayed_dump(&mid_info->mid_crashdump_td, mid_info, _timeout);
		if (res) {
			MLGE("MID: MID delayed crashdump thread creation failure: %s", strerror(res));
		}

		mid_info->flash_retry_left = mid_info->config->flash_retry;
		mid_info->boot_retry_left = mid_info->config->boot_retry;
		mid_info->boot_failed = 0;
		mid_info->flash_failed = 0;
		mid_info->state = STATE_ON;
		return MID_CLEAR_EVENT;
	}

	/* Need to handle runtime mode SERVICE specificaly */
	if (strncmp("service",mid_info->config->runtime_mode,strlen("service")) == 0) {
		mid_disable_gpio_service_safe_mode(mid_info);

		if (unlikely(mid_gpio_assert(mid_info->gpios, GPIO_ID_SERVICE))) {
			MLGE("GPIO: Can't assert GPIO_MOD_SERVICE");
			mid_enable_gpio_service_safe_mode(mid_info);
			return MID_CLEAR_EVENT;
		}

		if (my_system(mid_info, mid_info->config->mod_prefl_cmd) != 0) {
			/* Ignoring return values when cleaning up */
			(void)mid_gpio_deassert(mid_info->gpios, GPIO_ID_SERVICE);
			mid_enable_gpio_service_safe_mode(mid_info);
			(void)my_system(mid_info, mid_info->config->mod_postfl_cmd);
			return MID_CLEAR_EVENT;
		}
#if defined(MOTOROLA_FEATURE)
		(void)my_system(mid_info, mot_mid_info.svc_mode_cmd);
#endif

		if (modem_power_on(mid_info, 0) != 0) {
			MLGE("GPIO: Unable to power on modem!");
			/* Ignoring return values when cleaning up */
			(void)mid_gpio_deassert(mid_info->gpios, GPIO_ID_SERVICE);
			mid_enable_gpio_service_safe_mode(mid_info);
			(void)my_system(mid_info, mid_info->config->mod_postfl_cmd);
			return MID_CLEAR_EVENT;
		}

		MLGD("MID: Entered service mode, update mid.conf and restart host "
		     "in order to exit service mode");
		return MID_CLEAR_EVENT;
	}

	/* FLASH OPERATION TRIGGER */
	MLGD("isdirempty(\"%s\")==%d", mid_info->config->initial_rdir, isdirempty(mid_info->config->initial_rdir));
	MLGD("initial_enable_bootldr==%d", mid_info->config->initial_enable_bootldr);
	MLGD("flash_failed=%d", mid_info->flash_failed);
	if (!isdirempty(mid_info->config->initial_rdir) && (!mid_info->config->initial_enable_bootldr) && !mid_info->flash_failed) {
		union ipc_msg msg;
		if (!mid_statemachine_start_flashing(mid_info))
			return MID_KEEP_EVENT;
		MLGD("Entering FLASHING state");
		mid_info->state = STATE_FLASHING;
		ipc_prepare_message(&msg, STATECHANGE, "flashing", get_reason_from_last_event(mid_info));
		ipc_send_message(msg);
		return MID_CLEAR_EVENT;
	}

	if (mid_info->config->initial_enable_bootldr) {
		/* FLASHLESS VARIANT - FIRMWARE UPLOAD */
		MLGD("MID: MID bootloader started(Retries left:%d)",mid_info->boot_retry_left);

		if (mid_info->boot_retry_left == 0 && !mid_info->boot_failed) {
			mid_info->boot_retry_left = mid_info->config->boot_retry;
			mid_info->boot_failed = 1;
			if (mid_info->config->reboot_on_failure) {
#if defined(MOTOROLA_FEATURE)
				mid_statemachine_log_panic_apr("Subtype: MID bootloader: maximum number of retries reached.\n", mid_info->config->critical_panic_detection_file);
#endif
				mid_platform_reboot(mid_info);
			}
			MLGD("MID: Maximum(%d) number of retries reached", mid_info->config->boot_retry);
			MLGD("STMACH: MID will stay in STATE_OFF.");
			return MID_CLEAR_EVENT;
		} else if (mid_info->boot_retry_left > 0) {
			mid_info->boot_failed = 0;
		}

		mid_info->boot_retry_left--;

		if (my_system(mid_info, mid_info->config->mod_prefl_cmd) != 0)
			return MID_KEEP_EVENT;
		mid_info->long_ops_result = MID_FLBOOT_OK;

#ifdef PERF_MEASUREMENT
		if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemImagesStarted))
			MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");
#endif
		if (mid_configure_dev(mid_info->config->prim_dev_config, mid_info->config->prim_dev_config->devtype) != 0) {
			MLGE("MID: Error on primary device configuration");
			return MID_KEEP_EVENT;
		}

		if (mid_info->config->link_switch && (mid_configure_dev(mid_info->config->sec_dev_config,
						mid_info->config->sec_dev_config->devtype) != 0)) {
			MLGE("MID: Error on secondary device configuration");
			return MID_KEEP_EVENT;
		}

		if ((status = apr_thread_create(&mid_info->mid_flboot_td, NULL, mid_info->moli_boot, mid_info, mid_info->pool)) != APR_SUCCESS) {
			char message[100];
			MLGE("MID: MID moli_boot thread creation failure: %s",
				apr_strerror(status, message, sizeof(message)));
			(void)my_system(mid_info, mid_info->config->mod_postfl_cmd);
			shutdown_modem(mid_info);
			return MID_KEEP_EVENT;
		}
		mid_info->state = STATE_BOOT;
		return MID_KEEP_EVENT;
	}

	return mid_statemachine_try_boot(mid_info);
}

/***************
BOOT
***************/
MID_Event_Oper_t MID_State_Boot__Event_LONG_OPS(struct mid *mid_info)
{
	int res;
	union ipc_msg msg;
	MLGD("STMACH: MID execute Action: LONG_OPS_EVT processing. Firmware upload is completed.");

#ifdef PERF_MEASUREMENT
	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemImagesFinished))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");
#endif

	if (mid_info->long_ops_result != MID_FLBOOT_OK) {
		MLGE("MID: MID Failure on firmware upload. Try to boot again");
		(void)my_system(mid_info, mid_info->config->mod_postfl_cmd);
		shutdown_modem(mid_info);
		return MID_CLEAR_EVENT;
	}

	/* Flashless variant without gpio RST2 ctrl assume modem is ready and start RFM */
	/* With RST2 support we just stay in current state and wait for the RST2 event */
	MLGD("MID: MID Firmware upload completed");
	if (!mid_gpio_used(mid_info->gpios, GPIO_ID_RESOUT2)) {
		if (!mid_info->config->use_on_sw_c) {
		/* OnSwA - deassert power ON */
			if (mid_gpio_deassert(mid_info->gpios, GPIO_ID_ON))
				MLGE("GPIO: Can't deassert GPIO_ON");
		}

		res = my_system(mid_info, mid_info->config->mod_postfl_cmd);
		if (res != 0)
			return MID_CLEAR_EVENT;
		res = my_system(mid_info, mid_info->config->mod_ifup_cmd);
		if (res != 0) {
			shutdown_modem(mid_info);
			return MID_CLEAR_EVENT;
		}

		/* RFM will be started when receiving the booting ipc event */
		ipc_prepare_message(&msg, STATECHANGE, "booting", get_reason_from_last_event(mid_info));
		ipc_send_message(msg);

		if (mid_info->config->rfm_delay) {
			apr_status_t status;
			status = apr_thread_create(&mid_info->mid_rfmsleep_td, NULL, &rfm_sleep, mid_info, mid_info->pool);
			if (status != APR_SUCCESS) {
				char message[100];
				MLGE("MID: MID rfm_sleep thread creation failure: %s",
					apr_strerror(status, message, sizeof(message)));
				shutdown_modem(mid_info);
				return MID_CLEAR_EVENT;
			}
			mid_info->state = STATE_MODINF_UP;
			return MID_CLEAR_EVENT;
		}

		if (mid_info->config->enable_at_channel) {
			if (mid_at_chnl_open(mid_info)) {
				MLGE("AT: Unable to open AT channel. MID will shutdown the modem.");
				shutdown_modem(mid_info);
				return MID_CLEAR_EVENT;
			}
			mid_info->state = STATE_MODINF_UP;
			return MID_CLEAR_EVENT;
		}

		/* We just set the MID_CFRDY_EVT event and enter the MODINF_UP state so */
		/* so we can handle the code at the same place */

		SET_FLAG(mid_info->event, MID_CFRDY_EVT);
		MLGD("STMACH: MID_CFRDY_EVT event set.");
		mid_info->state = STATE_MODINF_UP;
		return MID_CLEAR_EVENT;
	}
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_Boot__Event_GPIO_RST2(struct mid *mid_info)
{
	int res;
	union ipc_msg msg;
	MLGD("STMACH: MID execute Action: GPIO_RST2_EVT processing.");

	if (mid_info->power_on_release) {
		/* OnSwA specific */
		MLGD("GPIO: De-assert MOD ON pin");
		res = mid_gpio_deassert(mid_info->gpios, GPIO_ID_ON);
		if (unlikely(res)) {
			MLGE("GPIO: Can't deassert GPIO_ON");
			mid_info->power_on_release = 0;
		}
		mid_info->power_on_release = 0;
	}

#ifdef PERF_MEASUREMENT
	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemRST2))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");
#endif


	if (!mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2)) {
		return MID_CLEAR_EVENT;
	}

	/* We only do this for flashless, for the flashed variant we don't execute prefl_ and postfl_cmd  */
	if (mid_info->config->initial_enable_bootldr) {
		res = my_system(mid_info, mid_info->config->mod_postfl_cmd);
		if (res != 0) {
			goto error_exit_shutdown;
		}
	}

	res = my_system(mid_info, mid_info->config->mod_ifup_cmd);
	if (res != 0) {
		goto error_exit_shutdown;
	}

	/* RFM will be started when receiving the booting ipc event */
	ipc_prepare_message(&msg, STATECHANGE, "booting", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);


	if (mid_info->config->rfm_delay) {
		apr_status_t status;
		status = apr_thread_create(&mid_info->mid_rfmsleep_td, NULL, &rfm_sleep, mid_info, mid_info->pool);
		if (status != APR_SUCCESS) {
			char message[100];
			MLGE("MID: MID rfm_sleep thread creation failure: %s",
				apr_strerror(status, message, sizeof(message)));
			goto error_exit_shutdown;
		}
		mid_info->state = STATE_MODINF_UP;
		goto clean_exit;
	}

	if (mid_info->config->enable_at_channel) {
		if (mid_at_chnl_open(mid_info)) {
			MLGE("AT: Unable to open AT channel. MID will shutdown the modem.");
			goto error_exit_shutdown;
		}
		mid_info->state = STATE_MODINF_UP;
		goto clean_exit;
	}

	/* We just set the MID_CFRDY_EVT event and enter the MODINF_UP state so */
	/* so we can handle the code at the same place */

	SET_FLAG(mid_info->event, MID_CFRDY_EVT);
	MLGD("STMACH: MID_CFRDY_EVT event set.");
	mid_info->state = STATE_MODINF_UP;

clean_exit:
	if (mid_info->fw_upgr_flash_reply_msg) {
		if (ipc_send_async_reply(&mid_info->fw_upgr_flash_reply_msg, "OK"))
			MLGE("DBUS: Failed to send fw upgrade reply");
	}

	return MID_CLEAR_EVENT;

error_exit_shutdown:
	shutdown_modem(mid_info);
	return MID_CLEAR_EVENT;
}

/*******************/
/* STATE MODINF_UP */
/*******************/
MID_Event_Oper_t MID_State_ModInfUp__Event_CFRDY(struct mid *mid_info)
{
	int res;
	union ipc_msg msg;

	MLGD("STMACH: MID execute Action: CFRDY_EVT processing. AT channel is ready to be used.");
#ifdef PERF_MEASUREMENT
	if (clock_gettime(CLOCK_MONOTONIC, &mid_info->modemCFRDY))
		MLGW("TIME: Error getting monotonic clock. Following time report information should be considered as wrong.");
#endif

	res = my_system(mid_info, mid_info->config->mod_on_cmd);
	if (res != 0) {
		shutdown_modem(mid_info);
		return MID_CLEAR_EVENT;
	}
	if (mid_info->config->enable_at_channel)
		if (mid_configure_at_chnl(mid_info)) {
			MLGE("AT: the channel cannot be configured. A whole modem power sequence is trigged.");
			shutdown_modem(mid_info);
			return MID_CLEAR_EVENT;
		}

	/* Clearing Reboot event and start event */
	mid_info->event &= ~(1<<MID_MODEM_START_EVT);
	MLGD("STMACH: MID_MODEM_START_EVT event cleared");
	mid_info->event &= ~(1<<MID_IPC_RBT_EVT);
	MLGD("STMACH: MID_IPC_RBT_EVT event cleared");

	if (mid_info->config->initial_sec_data_conf == 1 && mid_info->config->enable_at_channel) {
		(*mid_info->moli_configure_security_data)(mid_info);
	}

	mid_info->flash_retry_left = mid_info->config->flash_retry;
	mid_info->boot_retry_left = mid_info->config->boot_retry;
	mid_info->boot_failed = 0;
	mid_info->flash_failed = 0;
	mid_info->state = STATE_ON;
	if (mid_info->modem_warm_start == 1) {
		ipc_prepare_message(&msg, STATECHANGE, "warm_on", get_reason_from_last_event(mid_info));
		ipc_send_message(msg);
	}
	ipc_prepare_message(&msg, STATECHANGE, "on", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);
	mid_mfa_notify_on(mid_info->mfa);

#ifdef PERF_MEASUREMENT
		MLGD("TIME: Performance status");
		MLGD("-  MID Started: %4.2f",(float)(mid_info->midStarted.tv_sec+(float)(mid_info->midStarted.tv_nsec/1000000000)));
		MLGD("-  MID boot started: %4.2f",(float)(mid_info->modemBootStarted.tv_sec+(float)(mid_info->modemBootStarted.tv_nsec/1000000000)));
		MLGD("-  MID images tx started: %4.2f",(float)(mid_info->modemImagesStarted.tv_sec+(float)(mid_info->modemImagesStarted.tv_nsec/1000000000)));
		MLGD("-  MID images tx finished: %4.2f",(float)(mid_info->modemImagesFinished.tv_sec+(float)(mid_info->modemImagesFinished.tv_nsec/1000000000)));
		MLGD("-  MID RST2 received: %4.2f",(float)(mid_info->modemRST2.tv_sec+(float)(mid_info->modemRST2.tv_nsec/1000000000)));
		MLGD("-  MID CFRDY received: %4.2f",(float)(mid_info->modemCFRDY.tv_sec+(float)(mid_info->modemCFRDY.tv_nsec/1000000000)));
#endif
#if defined(MOTOROLA_FEATURE)
	/*
	 * Disable the UART connection to prevent UART TX from keeping
	 * the modem awake.
	 */
	mid_enable_io_safe_mode(mid_info);
#endif

	/* TODO: Consider remove this when crashdump is tested and working */
	if (mid_info->config->crashdump_dev_mode > 0) {
		if (mid_configure_dev(mid_info->config->prim_dev_config, mid_info->config->prim_dev_config->devtype) != 0) {
			MLGE("MID: Error on primary device configuration");
			return MID_CLEAR_EVENT;
		}

		if (mid_info->config->link_switch && (mid_configure_dev(mid_info->config->sec_dev_config,
						mid_info->config->sec_dev_config->devtype) != 0)) {
			MLGE("MID: Error on secondary device configuration");
			return MID_CLEAR_EVENT;
		}
		res = start_delayed_dump(&mid_info->mid_crashdump_td, mid_info, mid_info->config->crashdump_dev_mode);
		if (res) {
			MLGE("MID: MID delayed crashdump thread creation failure: %s", strerror(res));
		}
	}
	return MID_CLEAR_EVENT;
}

/*******************/
/* STATE ON         */
/*******************/

MID_Event_Oper_t MID_State_On_Event_AT_CFUN0_EVT(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: AT_CFUN0_EVT processing.");
	shutdown_modem(mid_info);
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_On_Event_AT_CFUN100_EVT(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: AT_CFUN100_EVT processing.");
	shutdown_modem(mid_info);
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_On__Event_IPC_RBT(struct mid *mid_info)
{
	MLGD("STMACH: MID execute Action: IPC_RBT_EVT processing.");
	mid_info->boot_retry_left = mid_info->config->boot_retry;
	/* Forced modem restart should _not_ trigger a warm restart indication */
	mid_info->modem_warm_start = 1;
	shutdown_modem(mid_info);

	return MID_KEEP_EVENT;
}

MID_Event_Oper_t MID_State_On_ModInfUp__Event_GPIO_RST2(struct mid *mid_info)
{
	union ipc_msg msg;
	int res;
	int attempts_left;
	int crashdump_result = 0;

	MLGD("STMACH: MID execute Action: GPIO_RST2_EVT processing.");

	if (mid_gpio_get_value(mid_info->gpios, GPIO_ID_RESOUT2)) {
		MLGW("GPIO: MID invalid state. GPIO RESOUT2 should be inactive not active. MID will do nothing, but problem could occur now.");
		return MID_CLEAR_EVENT;
	}

	MLGD("MID: Modem crash / shutdown detected.");

	if (!mid_gpio_used(mid_info->gpios, GPIO_ID_PWRRSTIN)) {
		if (mid_info->config->modem_shutdown_mode == SHUTDOWN_MODE_RESOUT2_MODEM_IS_OFF) {
			mid_info->modem_is_on = 0;
		}
	}


	if (mid_info->config->crash_dump_type == DISSABLE_CRASH_DUMP) {
		goto skip_modem_dump;
	}
#if defined(MOTOROLA_FEATURE)
	/*
	 * Enable the UART connection since it was previously disabled and
	 * it's used for crash dump.
	 */
	mid_disable_io_safe_mode(mid_info);
#endif

	ipc_prepare_message(&msg, STATECHANGE, "dumping", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);

	attempts_left = mid_info->config->boot_retry;

	do {
		attempts_left--;
		MLGD("MID: MID system CMD %s", mid_info->config->mod_off_cmd);
		res = my_system(mid_info, mid_info->config->mod_off_cmd);
		if (res != 0) {
			MLGE("MID: Failed to execute system call for mod_off_cmd. MID will continue anyway, but problem could occur on next modem on sequence.");
		}
#if defined(MOTOROLA_FEATURE)
		if (mot_mid_info.dual_boot_mode)
			continue;
#endif

		MLGD("MID: MID system CMD %s", mid_info->config->mod_pre_crashdump_cmd);
		res = my_system(mid_info, mid_info->config->mod_pre_crashdump_cmd);
		if (res != 0) {
			MLGE("MID: Failed to execute system call for mod_precrash_cmd. Crash dump may fail.");
		}
		MLGD("MID: Start retrieving crash dump");
		if (mid_configure_dev(mid_info->config->prim_dev_config, mid_info->config->prim_dev_config->devtype) != 0) {
			MLGE("MID: Error on primary device configuration");
			break;
		}

		if (mid_info->config->link_switch && (mid_configure_dev(mid_info->config->sec_dev_config,
						mid_info->config->sec_dev_config->devtype) != 0)) {
			MLGE("MID: Error on secondary device configuration");
			break;
		}

		crashdump_result = mid_info->moli_get_crash_dump(mid_info, mid_info->config->sec_dev_config, mid_info->config->dump_directory, mid_info->config->dump_directory_header, mid_info->config->dump_filename, mid_info->config->dump_filename_header, mid_info->config->dump_timestamp, mid_info->config->crash_dump_type, mid_info->config->max_dump_files);
		if (crashdump_result) {
			MLGE("MID: Failed retrieving crash dump report!!!  Result:%d",crashdump_result);
		} else {
			MLGD("MID: Successfully retrieved crash dump");
		}

		res = my_system(mid_info, mid_info->config->mod_post_crashdump_cmd);
		if (res != 0) {
			MLGE("MID: Failed to execute system call for mod_postrash_cmd");
		}


	} while (attempts_left>0 && crashdump_result != 0);

skip_modem_dump:
#if defined(MOTOROLA_FEATURE)
	mid_statemachine_log_panic_apr("Subtype: Modem silent panic without system reboot.\n", mid_info->config->silent_panic_detection_file);
#endif

	if (mid_info->config->enable_at_channel && (mid_info->at_chnl_fd >= 0))
		mid_at_chnl_close(mid_info);

	mid_info->event |= (1<<MID_MODEM_START_EVT);
	MLGD("STMACH: MID_MODEM_START_EVT event cleared");

	mid_info->modem_warm_start = 1;
	shutdown_modem(mid_info);

	return MID_CLEAR_EVENT;
}


MID_Event_Oper_t MID_State_On__Event_FW_UPGR_IMG_XFER(struct mid *mid_info)
{
	union ipc_msg msg;
	int res = 0;

	MLGD("STMACH: MID execute Action: FW_UPGR_IMG_XFER_EVT processing.");
	mid_info->fw_upgr_last_error = 0;

	res = mid_info->moli_fw_upgrade_img_xfer(mid_info);

	if (res) {
		if (mid_info->fw_upgr_img_xfer_reply_msg) {
			const char * error = mid_fw_upgrade_get_error_str(mid_info, mid_info->fw_upgr_last_error);
			if (ipc_send_async_reply(&mid_info->fw_upgr_img_xfer_reply_msg,
						 (error ? error : "ERROR"))) {
				MLGE("DBUS: MID_State_On__Event_FW_UPGR_IMG_XFER: Failed to send reply");
			}
		}
	}

	/* Sucessfully started image transfer, wait for image transfer finished event */

	/* MID_FW_XFER_OPS event will return result from thread uploading the firmware */
	/* IPC response sent in FW_XFER_OPS event handler which is handled in all states */

	ipc_prepare_message(&msg, FW_UPGR_STATUS, "image_upload");
	ipc_send_message(msg);

	return MID_CLEAR_EVENT;
}


MID_Event_Oper_t MID_State_On__Event_FW_UPGR_FLASH(struct mid *mid_info)
{
	union ipc_msg msg;
	int res = 0;
	MLGD("STMACH: MID execute Action: FW_UPGR_FLASH_EVT processing.");

	/* Check whether we're busy transfering the image */
	if (mid_info->fw_upgr_img_xfer_reply_msg) {
		if (ipc_send_async_reply(&mid_info->fw_upgr_flash_reply_msg,"ERROR: Transferring image is not finished"))
			MLGE("DBUS: MID_State_On__Event_FW_UPGR_FLASH: Failed to send reply");
		return MID_CLEAR_EVENT;
	}
	/* No firmware upgrade for flashless */
	if (mid_info->config->initial_enable_bootldr) {
		if (ipc_send_async_reply(&mid_info->fw_upgr_flash_reply_msg,"ERROR: Transferring image is not finished"))
			MLGE("DBUS: MID_State_On__Event_FW_UPGR_FLASH: Failed to send reply");
		return MID_CLEAR_EVENT;
	}

	/* We need to prepare for reboot and this will notify the other components to close down */
	(void)state_all_act_shutdown(mid_info,0);

	/* If we fail in any statement below we need to reboot  */
	/* since we have shutdown part(RIL, AT-Chanenl,..) of the system in the statement above */
	res = mid_info->moli_fw_upgrade_flash();
	if (res) {
		if (mid_info->fw_upgr_flash_reply_msg) {
			const char * error = mid_fw_upgrade_get_error_str(mid_info, mid_info->fw_upgr_last_error);
			if (ipc_send_async_reply(&mid_info->fw_upgr_flash_reply_msg,
						 (error ? error : "ERROR"))) {
				MLGE("DBUS: MID_State_On__Event_FW_UPGR_FLASH: Failed to send reply");
			}
		}
		SET_FLAG(mid_info->event, MID_IPC_RBT_EVT);
		MLGD("STMACH: MID_IPC_RBT_EVT event set.");
		shutdown_modem(mid_info);
		return MID_CLEAR_EVENT;
	}

	/* Sucessfull IPC event sent when receiving RESOUT2 in boot state */

	//ETOHEF
	//mid_info.state = STATE_FW_UPGRADE;
	mid_info->state = STATE_BOOT;
	ipc_prepare_message(&msg, FW_UPGR_STATUS, "modem_upgrade");
	ipc_send_message(msg);

	ipc_prepare_message(&msg, STATECHANGE, "upgrading", get_reason_from_last_event(mid_info));
	ipc_send_message(msg);

	MLGD("MID: MID system CMD %s", mid_info->config->mod_off_cmd);
	res = my_system(mid_info, mid_info->config->mod_off_cmd);
	if (res != 0) {
		MLGE("MID: Failed to execute system call for mod_off_cmd. MID will continue anyway, but problem could occur on next modem on sequence.");
	}
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t MID_State_On__Event_FW_UPGR_STATUS(struct mid *mid_info)
{
	char buf[128];
	int res;
	MLGD("STMACH: MID execute Action: FW_UPGR_STATUS_EVT processing.");
	if (mid_info->fw_upgr_status_string) {
		free(mid_info->fw_upgr_status_string);
		mid_info->fw_upgr_status_string = NULL;
	}

	res = mid_info->moli_fw_upgrade_status(&(mid_info->fw_upgr_status_string));

	/* No status string means the call failed, ret.val is not interesting */
	if (mid_info->fw_upgr_status_string) {
		snprintf(buf, sizeof(buf) - 1, "%s",
			 mid_info->fw_upgr_status_string);
		free(mid_info->fw_upgr_status_string);
		mid_info->fw_upgr_status_string = NULL;

		if (ipc_send_async_reply(&mid_info->fw_upgr_status_reply_msg, buf)) {
			MLGE("DBUS: MID_State_Off__Event_FW_UPGR: Failed to send reply");
		}
	} else {
		if (mid_info->fw_upgr_status_reply_msg) {
			const char * error = mid_fw_upgrade_get_error_str(mid_info, mid_info->fw_upgr_last_error);

			if (ipc_send_async_reply(&mid_info->fw_upgr_status_reply_msg,
						 (error ? error : "ERROR"))) {
				MLGE("DBUS: MID_State_Off__Event_FW_UPGR: Failed to send reply");
				// TODO: Handle how? Cannot inform caller!
			}
		}
	}
	return MID_CLEAR_EVENT;
}

MID_Event_Oper_t  MID_State_On__Event_FW_XFER_PROGRESS(struct mid *mid_info)
{
	union ipc_msg msg;
	MLGD("STMACH: MID execute Action: FW_XFER_PROGRESS_EVT processing.");

	ipc_prepare_message(&msg, FW_UPGR_XFER_PROGRESS, mid_info->fw_upgr_xfer_progress);
	ipc_send_message(msg);

	return MID_CLEAR_EVENT;
}

/******************/
/* STATE SHUTDOWN */
/******************/
/* Nothing to do */

static Event_Func_t Off_Transitions[] = {
	{MID_MODEM_START_EVT, MID_State_Off__Event_MODEM_START__IPC_RBT},
	{MID_SIG_KILL_EVT, MID_State_Off__Event_SIG_KILL},
	{MID_GPIO_RST2_EVT, NULL}, /* Do nothing, the event will be cleared */
	{MID_IPC_RBT_EVT, MID_State_Off__Event_MODEM_START__IPC_RBT},
	{MID_GPIO_PRST_EVT, NULL}, /* Do nothing, modem could be turned off during flash sequence */
	{MID_MODEM_HALT_EVT, NULL},
	{MID_LONG_OPS_EVT, UnExpected_Event},
	{MID_CFRDY_EVT, UnExpected_Event},
	{MID_HIGH_TEMPERATURE_EVT, UnExpected_Event},
	{MID_LOW_BATTERY_EVT, UnExpected_Event},
	{MID_AT_CFUN0_EVT, UnExpected_Event},
	{MID_AT_CFUN100_EVT, UnExpected_Event},
	{MID_IPC_FW_UPGR_IMG_XFER_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_FLASH_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_STATUS_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_FW_XFER_OPS_EVT, MID_State_Any__Event_FW_XFER_OPS},
	{MID_FW_XFER_PROGRESS_EVT, UnExpected_Event},
	{MID_TIMEOUT_EVT, UnExpected_Event},
	{MID_LAST_EVT, NULL}
};

static Event_Func_t Boot_Transitions[] = {
	{MID_SIG_KILL_EVT, MID_State_Any__Event_SIG_KILL},
	{MID_LONG_OPS_EVT, MID_State_Boot__Event_LONG_OPS},
	{MID_GPIO_RST2_EVT, MID_State_Boot__Event_GPIO_RST2},
	{MID_IPC_RBT_EVT, Save_Event},
	{MID_MODEM_START_EVT, Save_Event},
	{MID_GPIO_PRST_EVT, MID_State_Any__Event_GPIO_PRST_EVT},
	{MID_MODEM_HALT_EVT, Save_Event},
	{MID_CFRDY_EVT, UnExpected_Event},
	{MID_HIGH_TEMPERATURE_EVT, UnExpected_Event},
	{MID_LOW_BATTERY_EVT, UnExpected_Event},
	{MID_AT_CFUN0_EVT, UnExpected_Event},
	{MID_AT_CFUN100_EVT, UnExpected_Event},
	{MID_IPC_FW_UPGR_IMG_XFER_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_FLASH_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_STATUS_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_FW_XFER_OPS_EVT, MID_State_Any__Event_FW_XFER_OPS},
	{MID_FW_XFER_PROGRESS_EVT, UnExpected_Event},
	{MID_TIMEOUT_EVT, MID_State_Any__Event_TIMEOUT},
	{MID_LAST_EVT, NULL}
};

static Event_Func_t ModInfUp_Transitions[] = {
	{MID_SIG_KILL_EVT, MID_State_Any__Event_SIG_KILL},
	{MID_GPIO_RST2_EVT, MID_State_On_ModInfUp__Event_GPIO_RST2},
	{MID_CFRDY_EVT, MID_State_ModInfUp__Event_CFRDY},
	{MID_LONG_OPS_EVT, UnExpected_Event},
	{MID_IPC_RBT_EVT, Save_Event},
	{MID_MODEM_START_EVT, Save_Event},
	{MID_GPIO_PRST_EVT, MID_State_Any__Event_GPIO_PRST_EVT},
	{MID_MODEM_HALT_EVT, Save_Event},
	{MID_HIGH_TEMPERATURE_EVT, UnExpected_Event},
	{MID_LOW_BATTERY_EVT, UnExpected_Event},
	{MID_AT_CFUN0_EVT, UnExpected_Event},
	{MID_AT_CFUN100_EVT, UnExpected_Event},
	{MID_IPC_FW_UPGR_IMG_XFER_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_FLASH_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_STATUS_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_FW_XFER_OPS_EVT, MID_State_Any__Event_FW_XFER_OPS},
	{MID_FW_XFER_PROGRESS_EVT, UnExpected_Event},
	{MID_TIMEOUT_EVT, MID_State_Any__Event_TIMEOUT},
	{MID_LAST_EVT, NULL}
};

static Event_Func_t On_Transitions[] = {
	{MID_SIG_KILL_EVT, MID_State_Any__Event_SIG_KILL},
	{MID_IPC_RBT_EVT, MID_State_On__Event_IPC_RBT},
	{MID_GPIO_RST2_EVT, MID_State_On_ModInfUp__Event_GPIO_RST2},
	{MID_CFRDY_EVT, UnExpected_Event},
	{MID_LONG_OPS_EVT, UnExpected_Event},
	{MID_GPIO_PRST_EVT, MID_State_Any__Event_GPIO_PRST_EVT},
	{MID_MODEM_HALT_EVT, Save_Event},
	{MID_MODEM_START_EVT, UnExpected_Event},
	{MID_HIGH_TEMPERATURE_EVT, MID_State_Any__Event_HIGH_TEMPERATURE_EVT},
	{MID_LOW_BATTERY_EVT, MID_State_Any__Event_LOW_BATTERY_EVT},
	{MID_AT_CFUN0_EVT, MID_State_On_Event_AT_CFUN0_EVT},
	{MID_AT_CFUN100_EVT, MID_State_On_Event_AT_CFUN100_EVT},
	{MID_IPC_FW_UPGR_IMG_XFER_EVT, MID_State_On__Event_FW_UPGR_IMG_XFER},
	{MID_IPC_FW_UPGR_FLASH_EVT, MID_State_On__Event_FW_UPGR_FLASH},
	{MID_IPC_FW_UPGR_STATUS_EVT, MID_State_On__Event_FW_UPGR_STATUS},
	{MID_FW_XFER_OPS_EVT, MID_State_Any__Event_FW_XFER_OPS},
	{MID_FW_XFER_PROGRESS_EVT, MID_State_On__Event_FW_XFER_PROGRESS},
	{MID_TIMEOUT_EVT, UnExpected_Event},
	{MID_LAST_EVT, NULL}
};

static Event_Func_t Dump_Transitions[] = {
	{MID_SIG_KILL_EVT, UnExpected_Event},
	{MID_GPIO_RST2_EVT, UnExpected_Event},
	{MID_CFRDY_EVT, UnExpected_Event},
	{MID_LONG_OPS_EVT, UnExpected_Event},
	{MID_IPC_RBT_EVT, Save_Event},
	{MID_GPIO_PRST_EVT, MID_State_Any__Event_GPIO_PRST_EVT},
	{MID_MODEM_HALT_EVT, Save_Event},
	{MID_MODEM_START_EVT, UnExpected_Event},
	{MID_HIGH_TEMPERATURE_EVT, UnExpected_Event},
	{MID_LOW_BATTERY_EVT, UnExpected_Event},
	{MID_AT_CFUN0_EVT, UnExpected_Event},
	{MID_AT_CFUN100_EVT, UnExpected_Event},
	{MID_IPC_FW_UPGR_IMG_XFER_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_FLASH_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_STATUS_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_FW_XFER_OPS_EVT, MID_State_Any__Event_FW_XFER_OPS},
	{MID_FW_XFER_PROGRESS_EVT, UnExpected_Event},
	{MID_TIMEOUT_EVT, MID_State_Any__Event_TIMEOUT},
	{MID_LAST_EVT, NULL}
};

static Event_Func_t ShutDown_Transitions[] = {
	{MID_GPIO_RST2_EVT, NULL},
	{MID_GPIO_PRST_EVT, NULL},
	{MID_SIG_KILL_EVT, MID_State_Any__Event_SIG_KILL},
	{MID_MODEM_START_EVT, Save_Event},
	{MID_IPC_RBT_EVT, Save_Event},
	{MID_MODEM_HALT_EVT, Save_Event},
	{MID_LONG_OPS_EVT, UnExpected_Event},
	{MID_CFRDY_EVT, UnExpected_Event},
	{MID_HIGH_TEMPERATURE_EVT, UnExpected_Event},
	{MID_LOW_BATTERY_EVT, UnExpected_Event},
	{MID_AT_CFUN0_EVT, UnExpected_Event},
	{MID_AT_CFUN100_EVT, UnExpected_Event},
	{MID_IPC_FW_UPGR_IMG_XFER_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_FLASH_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_IPC_FW_UPGR_STATUS_EVT, MID_State_Any__Event_FW_UPGR},
	{MID_FW_XFER_OPS_EVT, MID_State_Any__Event_FW_XFER_OPS},
	{MID_FW_XFER_PROGRESS_EVT, UnExpected_Event},
	{MID_TIMEOUT_EVT, MID_State_Any__Event_TIMEOUT},
	{MID_LAST_EVT, NULL}
};

static Event_Func_t Flashing_Transitions[] = {
	{MID_FLASH_START_EVT, MID_State_Flashing__Event_FLASH_START},
	{MID_FLASH_END_EVT, MID_State_Flashing__Event_FLASH_END},
	{MID_GPIO_PRST_EVT, MID_State_Flashing__Event_GPIO_PRST},
	{MID_LAST_EVT, NULL}
};

static State_Entry_t MID_Transitions[] = {
	{Off_Transitions,         0},
	{Boot_Transitions,       60},
	{ModInfUp_Transitions,   60},
	{On_Transitions,          0},
	{Dump_Transitions,      180},
	{ShutDown_Transitions,   60},
	{Flashing_Transitions,    0},
};

void mid_statemachine_process_all_events(struct mid* mid_info, time_t* time_entered_state)
{
	size_t i=0;
	while (mid_info->event) {
		Event_Func_t *ev_func_p;
		Event_Func_t *func_array_p;

		if (! mid_prevent_suspend(mid_info->mid_power))
			MLGE("POW: Unable to prevent system to suspend. MID will continue anyway but power management may disturbe MID operation.");

		/* Get the pointer to the event state table based on current state */
		func_array_p = MID_Transitions[mid_info->state].event_func_info;

		ev_func_p = func_array_p+i;

		/* Check wether we have come to the end( {MID_LAST_EVT,NULL} ) of the state transition table. */
		if (ev_func_p->event == MID_LAST_EVT)
			break;

		/* Check whether this event should be handled */
		if ((1<<ev_func_p->event) & mid_info->event) {
			MID_State_t old_state = mid_info->state;

			if (ev_func_p->func == NULL) {
				MLGD("STMACH: Throwing event (%s). INFO: MID current state (%s)",MID_EventNames[ev_func_p->event],MID_StateNames[mid_info->state]);
				CLEAR_FLAG(mid_info->event,ev_func_p->event);
				i++;
			} else if (ev_func_p->func == Save_Event) {
				i++;
			} else {
				MID_Event_Oper_t evt_res;
				LOCK_MID_DATA;
				mid_info->cur_event = ev_func_p->event;
				MLGD("STMACH: MID will process event (%s). INFO: MID current state (%s)",MID_EventNames[ev_func_p->event],MID_StateNames[mid_info->state]);
				evt_res = ev_func_p->func(mid_info);
				if (evt_res == MID_CLEAR_EVENT)
					CLEAR_FLAG(mid_info->event,ev_func_p->event);
				mid_info->cur_event = -1;
				UNLOCK_MID_DATA;
				/* Starting all over again since new flags can have been set in above function call */
				i = 0;
			}
			if (old_state != mid_info->state) {
				MLGD("STMACH: MID STATE transition: (old) %s ---> %s (new)",MID_StateNames[old_state],MID_StateNames[mid_info->state]);
				/* We changed state and the TIMEOUT event was set means that we just got another event */
				/* before timeout, so we clear the event when entering the new state */
				CLEAR_FLAG(mid_info->event, MID_TIMEOUT_EVT);
				MLGD("STMACH: MID_TIMEOUT_EVT event cleared.");
				*time_entered_state = time(NULL);
			}
		} else {
			/* Check for next event */
			i++;
		}
	}
}

int mid_statemachine_get_current_timeout(struct mid* mid_info)
{
#if defined(MOTOROLA_FEATURE)
	if (mot_mid_info.dual_boot_mode && mid_info->state == STATE_BOOT)
		return 0;
#endif
	return MID_Transitions[mid_info->state].timeout_value;
}

const char* mid_statemachine_get_name_of_current_state(struct mid* mid_info)
{
	return MID_StateNames[mid_info->state];
}

#if defined(MOTOROLA_FEATURE)
void mid_statemachine_log_panic_apr(const char *panic_msg, const char *panic_file)
{
	FILE *aprlogfile = NULL;

	aprlogfile = fopen(panic_file, "wt");
	if (aprlogfile != NULL) {
		fchown(fileno(aprlogfile), AID_ROOT, AID_RADIO);
		fchmod(fileno(aprlogfile), 0660);
		fputs(panic_msg, aprlogfile);
		fclose(aprlogfile);
	}
}
#endif
